/**
 * @Author: Kritsu
 * @Date:   2021/11/13 11:27:11
 * @Last Modified by:   Kritsu
 * @Last Modified time: 2021/11/17 18:48:08
 */
/**
 *  词条描述
 */

import { to_percent } from "@/utils"
import { createItemColumn, EquipColumn, findEquipByName, getOptionValue } from "@/core/equips"
import { BufferEntry } from "@/core/entries/buffer"
import { StatusOptions } from "@/core/status"
import { CalcData, CalcListener } from "@/core/calc"
import { SkillType, SkillEntry, LEVEL_ADD, CDR } from "../skill"
import { Item, ItemColumn } from "../item"
import { useCalcStore } from "@/store"
import { DealerEntry } from "../entries/dealer"
import { EquipOption, EquipOptionData } from "./equip"
import { effectModifier, getModifiers } from "@/core/modifiers"
import { MythicEquip } from "./equip"

export interface Comment extends Record<string, string[]> {}

/**
 * 根据名称生成描述,并自动缓存
 * @param name  装备名称
 * @returns
 */
export function getCommentByEquipName(name?: string): Comment {
    let comment: Comment = {}
    if (!!name) {
        const equip = findEquipByName(name)
        if (!!equip) {
            comment = getCommentByEquipColumn(createItemColumn(equip) as EquipColumn)
        }
    }
    return comment
}

export function getCommentByItemColumn(column: ItemColumn): Comment {
    const comment: Comment = {}
    const { item, slots = [] } = column
    if (!item) {
        return comment
    }

    if (item.effect) {
        const context = new CalcData()

        item.effect(context)

        const effect_comment = getCommentByContext(context)
        comment["effect"] = effect_comment
    }

    if (!!item.dungeon_effect) {
        let context = new CalcData()

        item.dungeon_effect(context)
        const dungeon_effect_comment = getCommentByContext(context)
        comment["dungeon_effect"] = dungeon_effect_comment
    }

    for (let slot of slots) {
        let context = new CalcData()
        slot.effect(column, context)
        let history = Array.from(context.history())
        for (let i = 0; i < history.length; i++) {
            const key = i == 0 ? slot.name : `${slot.name}_${i}`
            let value = getCommentByContext(history[i])
            comment[key] = value
        }
    }

    return comment
}
/**
 * 根据装备生成描述
 * @param equip
 * @returns
 */
export function getCommentByEquipColumn(column: EquipColumn): Comment {
    const comment: Comment = getCommentByItemColumn(column)
    const { item } = column

    if (item?.modifier) {
        comment["modifier"] = getCommentByModifier(column)
    }

    if (item?.rarity == "mythic") {
        const lines: string[] = []
        const { mythic_properties } = item as MythicEquip
        for (let prop of mythic_properties) {
            const { deal_data, buff_data, effect } = prop
            const deal_value = getOptionValue(deal_data)

            const buff_value = getOptionValue(buff_data)
            const data = new CalcData()
            effect(data, [deal_value, buff_value])
            lines.push(...getCommentByContext(data))
        }
        comment["mythic"] = lines
    }

    return comment
}

export function getCommentByModifier(column: EquipColumn) {
    const { item, part, data } = column
    const lines: string[] = []

    if (item?.modifier) {
        let value = 0
        let [type, key, level = 3] = (data.get("modifier") as [string, string, number]) ?? ["none", "none", 0]

        if (type == "auto") {
            const { result } = useCalcStore()
            if (result?.context.modifiers) {
                let mod = result.context.modifiers.find(e => e[0] == part)
                if (mod) {
                    key = mod[1]
                    level = mod[2]
                }
            }
        }

        if (type == "none" || key == "none") {
            key = item.modifier[0]
            level = 0
        }

        value = item.modifier[1]

        const is_weapon = part == "weapon"

        const list = getModifiers(part, type)

        const modifier = list.find(e => e.name == key)

        if (modifier) {
            let { buff_data, key, name } = modifier

            let [deal_key, buff_key] = key

            if (deal_key != "awake") {
                const deal_unit = is_weapon ? 0.04 : 0.02

                value = name.startsWith("awake_") ? 0 : value

                const deal_start = value
                const deal_end = value.plus(deal_unit.multiply(4))

                const deal_data: EquipOptionData = [deal_unit, deal_start, deal_end]
                const deal_value = level > 0 ? getOptionValue(deal_data, level) : deal_start

                //输出属性
                const deal_string = formatLine(deal_key, deal_value)
                    .concat(" ")
                    .concat(`(${trim_number(deal_start.plus(deal_unit), "%")}~${trim_number(deal_end, "%")})`)
                lines.push(deal_string)

                if (buff_data) {
                    const buff_value = level > 0 ? getOptionValue(buff_data, level - 1) : 0 //buff要低1级
                    if (buff_value > 0) {
                        //辅助属性 当level = 0时 辅助属性不生效
                        const [_, buff_start, buff_end] = buff_data

                        const data = new CalcData()

                        data.changeBuff({
                            [buff_key]: buff_value
                        })

                        let buff_string = getCommentByContext(data)

                        buff_string = buff_string.map(e => e.concat(" ").concat(`(${trim_number(buff_start, "%")}~${trim_number(buff_end, "%")})`))
                        lines.push(...buff_string)
                    }
                }

                if (item.name == "weapon_epic_broom_01") {
                    const data = new CalcData()
                    data.changeBuff({
                        awake_level_add: 2
                    })
                    let buff_string = getCommentByContext(data)

                    lines.push(...buff_string)
                }
            }
            const data = new CalcData()
            modifier.effect(data, level)
            const fix_string = getCommentByContext(data)
            lines.push(...fix_string)
        }
    }
    return lines
}

export function trim_number(num: number, suffix = "") {
    return num > 0 && num < 1 ? to_percent(num, 0, suffix) : num.toString()
}

export function getCommentByItem(item?: Item) {
    if (item?.effect) {
        const data = new CalcData()
        item.effect(data)
        return getCommentByContext(data)
    }
    return []
}

export function getCommentByContext({ name, buff_entry, deal_entry, skill_entries, status, listeners }: CalcData): string[] {
    const comments = []
    if (!!name) {
        comments.push(name)
    }
    comments.push(...getCommentByStatus(status))

    comments.push(...getCommentsByDealerEntry(deal_entry))

    comments.push(...getCommentsBySkillEntries(skill_entries))

    comments.push(...getCommentsByBufferEntry(buff_entry))

    comments.push(...getCommentByListener(listeners))
    return comments
}

function getCommentByListener(listeners: Map<string, Set<CalcListener>>) {
    let comments: string[] = []

    for (let [key, funcs] of listeners) {
        const args = key.split(":")
        comments.push(`{on.${args[0]}}${args[1]}`)
        for (let func of funcs) {
            const data = new CalcData()
            func(data)
            comments.push(...getCommentByContext(data))
        }
    }
    return comments
}

function getCommentByStatus(options: StatusOptions): string[] {
    let { ice_boost = 0, fire_boost, light_boost, dark_boost, intelligence = 0, strength, vitality = 0, spirit, phyatk = 0, magatk, indatk } = options

    if (phyatk > 0 && [magatk, indatk].every(e => e == phyatk)) {
        options.triatk = phyatk
        delete options.phyatk
        delete options.magatk
        delete options.indatk
    }
    if (intelligence > 0 && intelligence == strength) {
        options.intstr = intelligence
        delete options.intelligence
        delete options.strength
    }
    if (vitality > 0 && vitality == spirit) {
        options.vitspi = vitality
        delete options.vitality
        delete options.spirit
    }
    if (options.vitspi && options.vitspi == options.intstr) {
        options.quadra = options.vitspi
        delete options.vitspi
        delete options.intstr
    }
    if (ice_boost > 0 && [fire_boost, light_boost, dark_boost].every(e => e == ice_boost)) {
        options.elemental_boost = ice_boost
        delete options.ice_boost
        delete options.fire_boost
        delete options.light_boost
        delete options.dark_boost
    }

    return formatObject(options, undefined, (k, v) => {
        let rs = `{${k}} +${v}`
        if (k.endsWith("_rate") || k.endsWith("_speed")) {
            rs = rs.concat("%")
        }
        return rs
    })
}

/**
 * 辅助专属属性
 * @param buff_entry
 * @returns
 */
function getCommentsBySkillEntries(skill_entries: SkillEntry[]): string[] {
    const comments: string[] = []
    const SPACE = " "
    for (let skill_entry of skill_entries) {
        const { start, end, type, condition, value } = skill_entry

        const line = []
        const { levels, ignores, jobs = [], names } = condition

        if (start + end > 1 || levels.length > 0) {
            line.push(SPACE)
            if (start > 0) {
                line.push(`Lv${start}`)
            }
            if (start < end) {
                line.push(`~${end}`)
                line.push(SPACE)
                line.push("{total}")
            }
            if (levels.length > 0) {
                line.push(levels.map(level => `Lv${level}`).join("、"))
            }

            line.push(SPACE)
            switch (condition.type) {
                case SkillType.ACTIVE:
                    line.push("{active_skill}")
                    break
                default:
                    line.push("{skill}")
                    break
            }
            line.push(SPACE)
        } else if (names.length == 0) {
            line.push("{all_skill}")
        }

        if (names.length > 0) {
            line.push(...names.map(e => `[{$skills.${e}}]{skill}`))
        }

        if (value > 0) {
            switch (type) {
                case LEVEL_ADD:
                    line.push("Lv")
                    line.push(SPACE)
                    line.push(`+${value}`)
                    break
                case CDR:
                    line.push("{cd}")
                    line.push(SPACE)
                    line.push(`-${to_percent(value, 0, "%")}`)
                    break
            }
        }

        comments.push(line.join(""))
    }
    return comments
}

/**
 * 辅助专属属性
 * @param buff_entry
 * @returns
 */
function getCommentsByDealerEntry(delear_entry: DealerEntry): string[] {
    const comments: string[] = []

    comments.push(...formatObject(delear_entry))

    return comments
}

/**
 * 辅助专属属性
 * @param buff_entry
 * @returns
 */
function getCommentsByBufferEntry(buff_entry: BufferEntry): string[] {
    const comments: string[] = []
    const { buff_phyatk_per, buff_magatk_per, buff_indatk_per, buff_int_per, buff_str_per } = buff_entry

    if (buff_int_per > 0 && buff_str_per > 0) {
        comments.push(`{buff_intstr_per[${to_percent(buff_str_per)}]} `)
    } else {
        comments.push(...formatObject({ buff_int_per, buff_str_per }))
    }

    if (buff_phyatk_per > 0 && buff_magatk_per > 0 && buff_indatk_per > 0) {
        comments.push(`{buff_triatk_per[${to_percent(buff_phyatk_per)}]} `)
    } else {
        comments.push(...formatObject({ buff_phyatk_per, buff_magatk_per, buff_indatk_per }))
    }

    const ignores = ["buff_int_per", "buff_str_per", "buff_phyatk_per", "buff_magatk_per", "buff_indatk_per"]
    comments.push(...formatObject(buff_entry, e => !ignores.includes(e)))

    return comments
}

function formatLine(key: string, value: number, mapper?: (key: string, value: string) => string) {
    const str = (value > 0 && value < 1 ? to_percent(value) : value.toString()) ?? ""
    if (!mapper) {
        mapper = (k, v) => `{${k}[${v}]}`
    }
    return mapper(key, str)
}
function formatObject(obj: any, predicate?: (val: string) => boolean, mapper?: (key: string, value: string) => string) {
    const comments = []

    let keys = Object.keys(obj)

    if (predicate) {
        keys = keys.filter(predicate)
    }

    for (let key of keys) {
        let value = obj[key] as number

        if (!!value) {
            comments.push(formatLine(key, value, mapper))
        }
    }
    return comments
}
